package ufr_stgi.assistance_conduite.synchronisation;

import java.io.*;
import java.net.*;
import java.util.*;

import javax.xml.parsers.*;

import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.*;
import org.xml.sax.*;

import ufr_stgi.assistance_conduite.MonApplication;
import ufr_stgi.assistance_conduite.R;
import ufr_stgi.assistance_conduite.modele.Panneau;
import ufr_stgi.assistance_conduite.modele.TypePanneau;

import android.app.*;
import android.content.Intent;
import android.os.Vibrator;
import android.util.Log;
import android.widget.*;

/**
 * Thread qui permet de synchroniser toutes les données (images, base de données, ...) en arrière-plan
 * @author Guillaume MONTAVON & Renaud DAVID (Master 2 RIM)
 */
public class Synchronisation extends Thread {
	
	public MonApplication appli;
	public static final int HTTP_STATUS_OK = 200;
	private int pourcentageTermine; //avancement actuel
	private int maxPourcentage; //avancement maximal
	private Notification notification;
	private NotificationManager notificationManager;
	private String messageNotif; //message à afficher lors d'une notification
	public static final int ACTION_MAJ_PANNEAU = 1;
	public static final int ACTION_IMPORTER_PANNEAU_GPX = 2;
	public static final int ACTION_MAJ_TYPE_PANNEAU = 3;
	public static final int ACTION_MAJ_IMAGES_PANNEAU = 4;
	public static final int ACTION_TOUT_SYNCHRONISER = 5;
	private int actionARealiser; //action que le Thread doit réaliser (voir les attributs ci-dessus)
	private String nomFichierGPX; //nom du fichier GPX à importer si on a décider d'importer un fichier
	private boolean reussi; //si l'opération s'est bien déroulée ou non
	
	public Synchronisation(MonApplication appli, int actionARealiser) {
		this.appli = appli;
		pourcentageTermine = 0;
		maxPourcentage = 100;
		this.actionARealiser = actionARealiser;
	}
	
	public Synchronisation(MonApplication appli, int actionARealiser, String nomFichierGPX) {
		this.appli = appli;
		pourcentageTermine = 0;
		maxPourcentage = 100;
		this.actionARealiser = actionARealiser;
		this.nomFichierGPX = nomFichierGPX;
	}
	
	@Override
	public void run() {
		super.run();
		
		appli.setEnCoursSynchro(true);
		
		reussi = false;
		
		switch (actionARealiser) {
			case ACTION_MAJ_PANNEAU :
				messageNotif = appli.getString(R.string.synchro_maj_panneaux);
				initNotification();
				reussi = MAJListePanneau();
				break;
			case ACTION_IMPORTER_PANNEAU_GPX :
				messageNotif = appli.getString(R.string.synchro_import_gpx);
				initNotification();
				reussi = importerFichierGPX(nomFichierGPX);//
				break;
			case ACTION_MAJ_TYPE_PANNEAU :
				messageNotif = appli.getString(R.string.synchro_type_panneaux);
				initNotification();
				reussi = importerTypePanneau();
				break;
			case ACTION_MAJ_IMAGES_PANNEAU :
				messageNotif = appli.getString(R.string.synchro_images_panneaux);
				initNotification();
				if(appli.reseauRapide())
					reussi = MAJImagesPanneau();
				else {
					appli.getMain().runOnUiThread(new Runnable() {	
						@Override
						public void run() {
							Toast.makeText(appli, messageNotif + appli.getString(R.string.notif_reseau_lent), Toast.LENGTH_SHORT).show();
						}
					});
				}
				break;
			case ACTION_TOUT_SYNCHRONISER :
				messageNotif = appli.getString(R.string.synchro_tout_synchroniser);
				initNotification();
				
				reussi = MAJListePanneau() && importerTypePanneau();
				
				if(appli.reseauRapide()) {
					if(!MAJImagesPanneau())
						reussi = false;
				}
				else {
					appli.getMain().runOnUiThread(new Runnable() {	
						@Override
						public void run() {
							Toast.makeText(appli, messageNotif + appli.getString(R.string.notif_reseau_lent), Toast.LENGTH_SHORT).show();
						}
					});
				}
				break;
			default:
				break;
		}
		
		appli.getMain().runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if(reussi) {
					notification.contentView.setTextViewText(R.id.status_progress, messageNotif + " " + appli.getString(R.string.notif_succes));
					Toast.makeText(appli, messageNotif + " " + appli.getString(R.string.notif_succes), Toast.LENGTH_SHORT).show();
				}
				else {
					notification.contentView.setTextViewText(R.id.status_progress, messageNotif + " " + appli.getString(R.string.notif_echouee));
					Toast.makeText(appli, messageNotif + " " + appli.getString(R.string.notif_echouee), Toast.LENGTH_SHORT).show();
				}
				appli.getMain().MAJCaracteristiques();
				appli.setEnCoursSynchro(false);
			}
		});
		
		notificationManager.cancel(MonApplication.CODE_NOTIFICATION_SYNCHRO);
		
	}

	/**
	 * Télécharge un fichier sur le serveur et l'enregistre sur la carte SD (souvent une image)
	 * @param parametreGET paramètre de requête GET à envoyer au serveur
	 * @param dossierDestination dossier de destination où sera enregistré le fichier
	 * @return true si tout s'est bien passé
	 */
	public synchronized boolean telechargerFichier(String parametreGET, File dossierDestination){
		Log.i("telechargerFichier", parametreGET);
		boolean reussi = false;
		
		try{
			HttpURLConnection conn = (HttpURLConnection) new URL(MonApplication.adresseSeveur + parametreGET).openConnection();
			conn.setRequestMethod("GET");
			conn.setDoOutput(true);
			conn.connect();
			InputStream is = conn.getInputStream();
			
			//this is the total size of the file
			//int totalSize = conn.getContentLength();
			int downloadedSize = 0;
			
			String nomFichier = conn.getHeaderField("Content-Disposition").split("=")[1].trim().replaceAll("\"", ""); //obtenir nom du fichier
			Log.i("content-size", ""+conn.getContentLength());
			
			FileOutputStream fos = new FileOutputStream(new File(dossierDestination, nomFichier));
			
			byte[] buffer = new byte[1024];
			int bufferLength = 0;
			
			//now, read through the input buffer and write the contents to the file
			while ((bufferLength = is.read(buffer)) > 0) {
				fos.write(buffer, 0, bufferLength);
				downloadedSize += bufferLength;
				//updateProgress(downloadedSize, totalSize); //progression
				//Log.i("pogress", downloadedSize+"/"+totalSize);
			}
			fos.close();
			
			reussi = true;
			Log.i("dl",nomFichier + " : OK");
		
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		return reussi;
		
	}
	
	/**
	 * Importe dans la base de données les panneaux contenus dans la chaîne de caractère donnée en paramètre (format GPX)
	 * @param gpx chaîne de caractère donnée en paramètre (format GPX)
	 * @return true si tout s'est bien passé
	 */
	public boolean importerPanneau(String gpx) {
		
		boolean reussi = false;
		
		try {
			
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser parser = factory.newSAXParser();
			XMLReader xmlreader = parser.getXMLReader();
			
			PanneauHandler panneauHandler = new PanneauHandler();
			
			xmlreader.setContentHandler(panneauHandler);
			xmlreader.parse(new InputSource(new StringReader(gpx)));
			
			ArrayList<Panneau> liste = panneauHandler.getListePanneau();
			
			Log.i("nb panneau", ""+liste.size());
			
			reussi = appli.getBdd().ajouterListePanneaux(liste);
			
			//ajoute dans la liste les images qui manquent
			//appli.getBdd().ajouterListeImageManquante(liste);
			
		} catch (ParserConfigurationException e) {
			Log.i("XML parse error", e.getMessage());
		} catch (SAXException e) {
			Log.i("Wrong XML file structure", e.getMessage());
		} catch (IOException e) {
			Log.i("I/O exeption", e.getMessage());
		}
		
		return reussi;
		
	}
	
	/**
	 * Récupère et retourne le contenu d'une page WEB
	 * @param requete requête GET à envoyer au serveur
	 * @param nvps liste de variables qui seront envoyé en POST
	 * @return true si tout s'est bien passé
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public synchronized String getHttp(String requete, List <NameValuePair> nvps) throws ClientProtocolException, IOException {
		
		DefaultHttpClient httpClient = new DefaultHttpClient();
		
		/*
		//configuration du proxy
		HttpHost proxy = new HttpHost(PreferenceManager.getDefaultSharedPreferences(context).getString("adresse_proxy", ""), PreferenceManager.getDefaultSharedPreferences(context).getInt("port", 0000));
		if(PreferenceManager.getDefaultSharedPreferences(context).getBoolean("utilise_proxy", false))
			httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
		*/
		
		Log.i("getHttp", MonApplication.adresseSeveur + requete);
		
		String reponse = "";
		
		try {
			HttpResponse res;
			
			if (nvps != null){
				HttpPost methodpost = new HttpPost(MonApplication.adresseSeveur + requete);
				methodpost.addHeader("pragma","no-cache");
				methodpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
				
				//timeout
				HttpConnectionParams.setConnectionTimeout(methodpost.getParams(), 5000);
				HttpConnectionParams.setSoTimeout(methodpost.getParams(), 20000);
				
				res = httpClient.execute(methodpost);
			} else {
				HttpGet methodget = new HttpGet(MonApplication.adresseSeveur + requete);
				methodget.addHeader("pragma","no-cache");
				
				//timeout
				HttpConnectionParams.setConnectionTimeout(methodget.getParams(), 5000);
				HttpConnectionParams.setSoTimeout(methodget.getParams(), 20000);
				
				res = httpClient.execute(methodget);
			}
			
			StatusLine status = res.getStatusLine();
			if (status.getStatusCode() != HTTP_STATUS_OK)
				Log.e("Erreur de réception", status.toString());
			
			reponse = EntityUtils.toString(res.getEntity());
			
		} catch (ClientProtocolException e) {
			Log.e("Erreur de réception", ""+e);
		} catch (IOException e) {
			Log.e("Erreur de réception", ""+e);
		}
		
		Log.i("réponse", reponse);
		
		return reponse;
		
	}
	
	/**
	 * Demande au serveur la liste des panneaux qui se trouvent dans notre zone puis les met à jour dans le Smartphone
	 * @return true si tout s'est bien passé
	 */
	public boolean MAJListePanneau() {
		
		boolean reussi = false;
		
		String gpx = "";
		try {
			gpx = getHttp("getListePanneau&lat=" + appli.getLatitude() + "&lng=" + appli.getLongitude(), null);
			pourcentageTermine = 50;
			MAJPourcentage();
			reussi = importerPanneau(gpx);
			pourcentageTermine = 100;
			MAJPourcentage();
			
		} catch (UnsupportedEncodingException e) {
			Log.e("Erreur", ""+e);
		} catch (MalformedURLException e) {
			Log.e("Erreur", ""+e);
		} catch (IOException e) {
			Log.e("Erreur", ""+e);
		}
		
		return reussi;
		
	}
	
	/**
	 * Importe un fichier GPX se trouvant sur la carte SD, extrait la liste des panneaux puis les enregistre dans le Smartphone
	 * @param nomFichier nom du fichier contenant le fichier GPX (sur la carte SD)
	 * @return true si tout s'est bien passé
	 */
	public boolean importerFichierGPX(String nomFichier) {
		
		boolean reussi = false;
		
		FileReader fis = null;
		
		try {
			StringWriter writer = new StringWriter();
			fis = new FileReader(new File(appli.getDossierImage(), nomFichier));
			BufferedReader br = new BufferedReader(fis);
			
			String line="";
			while ((line = br.readLine()) != null)
				writer.write(line);
				
			reussi = importerPanneau(writer.toString());
			
		} catch (FileNotFoundException e) {
			Log.e("Erreur", ""+e);
		} catch (IOException e) {
			Log.e("Erreur", ""+e);
		} finally {	 
			if(fis != null) {	   
				try {		   
					fis.close();		 
				} catch(IOException e) {			
					Log.e("Erreur", ""+e);
				}   
			} 
		}
		
		return reussi;
		
	}
	
	/**
	 * Met à jour les types de panneaux, existants sur le serveur, sur le Smartphone
	 * @return true si tout s'est bien passé
	 */
	public boolean importerTypePanneau() {
		
		boolean reussi = false;
		
		String listeTypePanneauConnus = "{\"listeType\":[";
		
		boolean premier = true;
		
		for(TypePanneau tp : appli.getBdd().getListeType()) {
			if(premier)
				premier = false;
			else
				listeTypePanneauConnus += ",";
			listeTypePanneauConnus += tp.getIdentifiant();
		}
		
		listeTypePanneauConnus += "]}";
		
		Log.i("parametres post", listeTypePanneauConnus);
		
		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();  
		nameValuePairs.add(new BasicNameValuePair("typePanneauConnus", listeTypePanneauConnus));  
		String contenu = "";
		try {
			contenu = getHttp("getTypeManquant", nameValuePairs);
		} catch (ClientProtocolException e) {
			Log.e("ClientProtocolException", ""+e);
		} catch (IOException e) {
			Log.e("JSONException", ""+e);
		}
		
		if(!contenu.equals("")) {
		
			ArrayList<TypePanneau> listeTP;
			try {
				listeTP = parseJSONTypePanneau(contenu);
				
				pourcentageTermine += 2;
				maxPourcentage = listeTP.size() + 2;
				MAJPourcentage();
				
				//téléchargement et enregistrement de l'image et du type dans la bdd
				for(TypePanneau tp : listeTP) {
					if(telechargerFichier("getImageType&idType=" + tp.getIdentifiant(), appli.getDossierImage()))
						appli.getBdd().ajouterType(tp, false);
					maxPourcentage++;
					MAJPourcentage();
				}
				reussi = true;
			
			} catch (JSONException e) {
				Log.e("JSONException", ""+e);
			}
		
		}
		return reussi;
	}
	
	/**
	 * importer les données encodées dans un format JSON, dans le modèle de l'application
	 * @param contenu données encodées en JSON
	 * @throws JSONException
	 */
	public ArrayList<TypePanneau> parseJSONTypePanneau(String contenu) throws JSONException	{
		
		ArrayList<TypePanneau> tp = null;
		
		if (!contenu.equals("")) {
			
			tp = new ArrayList<TypePanneau>();
			
			JSONObject ob = new JSONObject(contenu);
			JSONArray types = ob.getJSONArray("listeType");
			JSONObject type;
			
			for (int i = 0; i < types.length(); i++) {
				type = types.getJSONObject(i);
				tp.add(new TypePanneau(type.getLong("id"), type.getString("libelle"), type.getString("photo")));
			}
			
		}
		return tp;

	}
	
	
	/**
	 * Télécharge sur le serveur les images des panneaux manquants sur le Smartphone
	 * @return true si tout s'est bien passé
	 */
	public boolean MAJImagesPanneau() {
		
		boolean reussi = true;
		
		ArrayList<Long> listeImagesManquantes = appli.getBdd().getListeImageManquante();
		
		maxPourcentage = listeImagesManquantes.size();
		
		for(Long id : listeImagesManquantes) {
			Log.i("image no"+id, "image no"+id);
			if(existeImage(appli.getBdd().getPanneau(id).getChemin()))
				appli.getBdd().supprimerImageManquante(id);
			else if(telechargerFichier("getImagePanneau&id=" + id, appli.getDossierImage()))
				appli.getBdd().supprimerImageManquante(id);
			else
				reussi = false;
			pourcentageTermine++;
			MAJPourcentage();
		}
				
		return reussi;
		
	}
	
	/**
	 * Vérifie si une image existe sur la carte SD
	 * @param nomFichier nom de l'image recherchée
	 * @return true si l'image est présente sur la carte SD
	 */
	public boolean existeImage(String nomFichier) {
		
		return new File(appli.getDossierImage(), nomFichier).exists();
		
	}
	
	/**
	 * Met à jour l'avancement de l'opération dans la barre de notification
	 */
	public void MAJPourcentage() {
	
		notification.contentView.setProgressBar(R.id.status_progress, maxPourcentage, pourcentageTermine, false);
		notificationManager.notify(MonApplication.CODE_NOTIFICATION_SYNCHRO, notification);
		if(pourcentageTermine >= maxPourcentage) {
			Vibrator v = (Vibrator) appli.getSystemService(MonApplication.VIBRATOR_SERVICE); // Get instance of Vibrator from current Context
			v.vibrate(300); // Vibrate for 300 milliseconds
		}
	}
	
	/**
	 * Initialisation de l'avancement de l'opération dans la barre de notification
	 */
	public void initNotification() {
		
		Intent intent = new Intent(appli, Synchronisation.class);
		final PendingIntent pendingIntent = PendingIntent.getActivity(appli.getApplicationContext(), 0, intent, 0);
		// configure the notification
		notification = new Notification(R.drawable.pan, messageNotif, System.currentTimeMillis());
		notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
		//notification.defaults |= Notification.DEFAULT_SOUND;
		//notification.defaults |= Notification.DEFAULT_VIBRATE;
		notification.contentView = new RemoteViews(appli.getApplicationContext().getPackageName(), R.layout.upload_progress);
		notification.contentIntent = pendingIntent;
		notification.contentView.setImageViewResource(R.id.status_icon, R.drawable.pan);
		notification.contentView.setTextViewText(R.id.status_text, messageNotif + appli.getString(R.string.notif_en_cours));
		notification.contentView.setProgressBar(R.id.status_progress, maxPourcentage, 0, false);
		
		notificationManager = (NotificationManager) appli.getApplicationContext().getSystemService(MonApplication.NOTIFICATION_SERVICE);
		notificationManager.notify(MonApplication.CODE_NOTIFICATION_SYNCHRO, notification);
		
	}
	
}
